package net.ikenway.springboot.demo.Config;

import com.google.gson.Gson;
import net.ikenway.springboot.demo.Model.Dto.ResponseBodyDto;
import net.ikenway.springboot.demo.Service.Implement.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.token.Sha512DigestUtils;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author MasterKenway <zk@ikenway.net>
 * @Description
 * @date Created Date 12/10/2020
 */
@Configuration
@EnableConfigurationProperties(value = {
        JwtConfigurationProperties.class,
        WebDirectoryConfigurationProperties.class,
        WechatOAuthConfigurationProperties.class,
        TencentValidateConfigurationProperties.class})
@ComponentScan
public class SpringbootSecurityConfig extends WebSecurityConfigurerAdapter {
    private UserDetailsService userDetailsService;
    private CustomAccessDeniedHandler customAccessDeniedHandler;
    private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;

    @Autowired
    public void setUserDetailsService(UserDetailsServiceImpl userDetailsService,
                                      CustomAccessDeniedHandler customAccessDeniedHandler,
                                      CustomAuthenticationEntryPoint customAuthenticationEntryPoint) {
        this.userDetailsService = userDetailsService;
        this.customAccessDeniedHandler = customAccessDeniedHandler;
        this.customAuthenticationEntryPoint = customAuthenticationEntryPoint;
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/css/**", "/img/**", "/js/**", "/*.html");
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new PasswordEncoder() {
            @Override
            public String encode(CharSequence charSequence) {
                return Sha512DigestUtils.shaHex(charSequence.toString());
            }

            @Override
            public boolean matches(CharSequence charSequence, String s) {
                // 验证密码
                return s.equals(Sha512DigestUtils.shaHex(charSequence.toString()));
            }
        };
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        super.configure(auth);
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .cors().and()
                .csrf().disable()
                .apply(new JwtAuthConfigurer<>()).and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeRequests().antMatchers("/static/**").permitAll().and()
                .authorizeRequests().antMatchers("/*.html").permitAll().and()
                .authorizeRequests().antMatchers("/api/auth/login").permitAll().and()
                .authorizeRequests().antMatchers(HttpMethod.POST, "/api/auth/**").permitAll().and()
                .authorizeRequests().antMatchers(HttpMethod.GET, "/api/image/**").permitAll().and()
                .authorizeRequests().antMatchers(HttpMethod.GET, "/api/excels/**").permitAll().and()
                .authorizeRequests().anyRequest().authenticated().and()
                .exceptionHandling().accessDeniedHandler(customAccessDeniedHandler).and()
                .exceptionHandling().authenticationEntryPoint(customAuthenticationEntryPoint);
    }


    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
        return source;
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Component
    public static class CustomAccessDeniedHandler implements AccessDeniedHandler {
        @Override
        public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
            httpServletResponse.setContentType("application/json");
            Gson gson = new Gson();
            httpServletResponse.getWriter().write(gson.toJson(new ResponseBodyDto(400, "无权限访问")));
        }
    }

    @Component
    public static class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {

        @Override
        public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
            httpServletResponse.setContentType("application/json");
            Gson gson = new Gson();
            httpServletResponse.getWriter().write(gson.toJson(new ResponseBodyDto(400, e.getMessage())));
        }
    }

    //    处理 register api 请求时 403 的错误，最后通过 disable csrf 正常
    //    避免其他 Headers 未被设置进白名单 故注释掉
    //
//    @Bean
//    CorsConfigurationSource corsConfigurationSource() {
//        CorsConfiguration corsConfiguration = new CorsConfiguration();
//        corsConfiguration.addAllowedHeader("Content-Type");
//        UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
//        corsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
//        return corsConfigurationSource;
//    }

}
